#ifndef NANE_FUNCTIONPTR_H
#   define NANE_FUNCTIONPTR_H

#   include "nane/core/function/FunctionBase.h"

namespace nane
{

    template<typename RT, typename P1 = void,
                          typename P2 = void,
                          typename P3 = void,
                          typename P4 = void>
    struct FunctionPtrT
    {
        typedef RT (*Type)(P1,P2,P3,P4);
    };

    template<typename RT, typename P1, typename P2, typename P3>
    struct FunctionPtrT<RT, P1, P2, P3, void>
    {
        typedef RT (*Type)(P1,P2,P3);
    };

    // partial specialization for two parameters:
    template<typename RT, typename P1, typename P2>
    struct FunctionPtrT<RT, P1, P2, void, void>
    {
        typedef RT (*Type)(P1,P2);
    };

    // partial specialization for one parameter:
    template<typename RT, typename P1>
    struct FunctionPtrT<RT, P1, void, void, void>
    {
        typedef RT (*Type)(P1);
    };

    // partial specialization for no parameters:
    template<typename RT>
    struct FunctionPtrT<RT, void, void, void, void>
    {
        typedef RT (*Type)();
    };

    template<typename RT
           , typename P1 = void
           , typename P2 = void
           , typename P3 = void
           , typename P4 = void>
    class FunctionPtr
    {
    public:
        typedef typename FunctionPtrT<RT,P1,P2,P3,P4>::Type TFuncPtr;
        typedef RT TReturn;
        typedef P1 TParam1;
        typedef P2 TParam2;
        typedef P3 TParam3;
        typedef P4 TParam4;

    private:
        // the encapsulated pointer:
        TFuncPtr m_function;

    public:
        FunctionPtr( TFuncPtr _ptr )
            : m_function( _ptr )
        {
        }

        TReturn Call()
        {
            return (*m_function)();
        }

        TReturn Call( typename ForwardParamT<TParam1>::Type _a1 )
        {
            return (*m_function)(_a1);
        }

        TReturn Call( typename ForwardParamT<TParam1>::Type _a1
                    , typename ForwardParamT<TParam2>::Type _a2 )
        {
            return (*m_function)(_a1, _a2);
        }

        TReturn Call( typename ForwardParamT<TParam1>::Type _a1
                    , typename ForwardParamT<TParam2>::Type _a2
                    , typename ForwardParamT<TParam3>::Type _a3 )
        {
            return (*m_function)(_a1, _a2, _a3);
        }

        TReturn call( typename ForwardParamT<TParam1>::Type _a1
                    , typename ForwardParamT<TParam2>::Type _a2
                    , typename ForwardParamT<TParam3>::Type _a3
                    , typename ForwardParamT<TParam4>::Type _a4 )
        {
            return (*m_function)(_a1, _a2, _a3, _a4);
        }
    };

    template<typename RT> inline
    Functor< FunctionPtr<RT> > MakeFunctor( RT (*fp)() )
    {
        return Functor< FunctionPtr<RT> >(FunctionPtr<RT>(fp));
    }

    template<typename RT, typename P1> inline
    Functor< FunctionPtr<RT, P1> > MakeFunctor( RT (*fp)(P1) )
    {
        return Functor< FunctionPtr<RT, P1> >(FunctionPtr<RT, P1>(fp));
    }

    template<typename RT, typename P1, typename P2> inline
    Functor< FunctionPtr<RT, P1, P2> > MakeFunctor( RT (*fp)(P1,P2) )
    {
        return Functor< FunctionPtr<RT, P1, P2> >(FunctionPtr<RT, P1, P2>(fp));
    }

    template<typename RT, typename P1, typename P2, typename P3> inline
    Functor< FunctionPtr<RT, P1, P2, P3> > MakeFunctor( RT (*fp)(P1,P2,P3) )
    {
        return Functor< FunctionPtr<RT, P1, P2, P3> >(FunctionPtr<RT, P1, P2, P3>(fp));
    }

    template<typename RT, typename P1, typename P2, typename P3, typename P4> inline
    Functor< FunctionPtr<RT, P1, P2, P3, P4> > MakeFunctor( RT (*fp)(P1,P2,P3,P4) )
    {
        return Functor< FunctionPtr<RT, P1, P2, P3, P4> >(FunctionPtr<RT, P1, P2, P3, P4>(fp));
    }

}   // namespace nane

#endif	// NANE_FUNCTIONPTR_H
